﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.ComponentModel;
using Microsoft.Xna.Framework.Input;
using BEPUphysics;

namespace The_LarX
{
    public class RotatedPhysicFPSCamera : Camera3D
    {
#if WINDOWS
        [Category("Transformação")]
        [Description("A velocidade de movimentação da Câmera")]
#endif
        public float MovementSpeed
        {
            get { return speed; }
            set { speed = value; }
        }
        protected float speed = 30;

#if WINDOWS
        [Category("Renderização")]
        [Description("A distância que deve ser mostrada da Câmera")]
#endif
        public float FarPlane
        {
            get { return farPlane; }
            set { farPlane = value; }
        }
        protected float farPlane = 70;

        #region Rotation
        protected float leftrightRot = MathHelper.PiOver2;
        protected float updownRot = -MathHelper.Pi / 10.0f;
        protected float rotationSpeed = 5;

        public float LeftRightRot { get; set; }
        public float UpDownRot { get; set; }

        public override Vector3 Position
        {
            get
            {
                return Box.Position;
            }
            set
            {
                Box.Position = value;
            }
        }

#if WINDOWS
        [Category("Transformação")]
        [Description("A velocidade da rotação")]
#endif
        public float RotationSpeed
        {
            get { return rotationSpeed; }
            set { rotationSpeed = value; }
        }
        #endregion

        public BepuEntity Box;

        public Vector3 Velocity
        {
            get { return Vector3.Zero; }
            set
            {
                Box.Entity.LinearVelocity = value;
            }
        }
        public float DirectionVelocity
        {
            get { return 0; }
            set
            {
                Vector3 Direction = Vector3.Zero;
                Direction.X = -(float)Math.Sin(Actor.RadiansRotation.Y);
                Direction.Z = -(float)Math.Cos(Actor.RadiansRotation.Y);
                Box.Entity.LinearVelocity = Direction * value;
            }
        }

        protected Vector2 lastMousePos;
        protected MouseState originalMouseState;

        public RotatedPhysicFPSCamera(GameScreen Screen)
            : base(Screen)
        {
            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Engine.AspectRatio, 1.0f, farPlane);

            Target = new Vector3(0, 0, 0);

            Box = new BepuEntity_Box();
            Box.Position = new Vector3(0, 5, 0);

            Actor.ComponentParent = this;
            Actor.Entity = Box;
        }
        public RotatedPhysicFPSCamera()
        {
            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Engine.AspectRatio, 1.0f, farPlane);

            Target = new Vector3(0, 0, 0);

            Box = new BepuEntity_Box();
            Box.Position = new Vector3(0, 5, 0);

            Actor.ComponentParent = this;
            Actor.Entity = Box;
        }

        public override void Update()
        {
            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Engine.AspectRatio, 1.0f, farPlane);
           
            if (Engine.PlayMode)
            {
                float timeDifference = (float)Engine.GameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
                ProcessInput(timeDifference);
                originalMouseState = Mouse.GetState();
                lastMousePos = new Vector2(originalMouseState.X, originalMouseState.Y);                          
            }
            else
            {
                //Box.Entity.CenterPosition = Actor.Position;
                //Box.Entity.OrientationMatrix = Actor.RotationMatrix;               
                Box.Entity.LinearVelocity = Vector3.Zero;
                Box.Entity.AngularVelocity = Vector3.Zero;              
            }

            base.Update();
        }
        private void ProcessInput(float amount)
        {
            MouseState currentMouseState = Mouse.GetState();
            Vector2 newMousePos = new Vector2(currentMouseState.X, currentMouseState.Y);
            if (lastMousePos != newMousePos)
            {
                float xDifference = newMousePos.X - lastMousePos.X;
                float yDifference = newMousePos.Y - lastMousePos.Y;
                float X = MathHelper.ToRadians(xDifference);
                float Y = MathHelper.ToRadians(yDifference);
                leftrightRot -= rotationSpeed * X * amount;
                updownRot -= rotationSpeed * Y * amount;
                Mouse.SetPosition(Engine.GraphicsDevice.Viewport.Width / 2, Engine.GraphicsDevice.Viewport.Height / 2);
                UpdateViewMatrix();
            }

            Vector3 moveVector = new Vector3(0, 0, 0);
            KeyboardState keyState = Keyboard.GetState();
            bool called = false;
            if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W))
            {
                Box.DirectionVelocity = 5;
                called = true;
            }
            if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S))
            {
                Box.DirectionVelocity = -5;
                called = true;
            }
            if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D))
            {
                moveVector += new Vector3(1, 0, 0);
                called = true;
            }
            if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A))
            {
                moveVector += new Vector3(-1, 0, 0);
                called = true;
            }
        }
        private void AddToCameraPosition(Vector3 vectorToAdd)
        {
            Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
            Vector3 rotatedVector = Vector3.Transform(vectorToAdd, cameraRotation);            
            //Box.Velocity += speed * rotatedVector;
            Box.Velocity += speed * rotatedVector;
            Box.Velocity += new Vector3(0, 0, 0);
            
            UpdateViewMatrix();
        }

        private void UpdateViewMatrix()
        {
            Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);

            Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
            Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
            Vector3 cameraFinalTarget = Position + cameraRotatedTarget;

            Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
            Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);

            View = Matrix.CreateLookAt(Position, cameraFinalTarget, cameraRotatedUpVector);
        }

        public override void SetToInitialState()
        {
            base.SetToInitialState();

            Actor.SetToInitialState();
        }


        public override void SerializeData()
        {
            base.SerializeData();

            Data.AddData("RotatedCamera.Position", this.Position);
            Data.AddData("RotatedCamera.Target", this.Target);
            Data.AddData("RotatedCamera.LeftRightRot", this.leftrightRot);
            Data.AddData("RotatedCamera.UpDownLot", this.updownRot);

            Box.SerializeData(Data);
        }
        public override void DeserializeData()
        {
            base.DeserializeData();

            this.Position = Data.GetData<Vector3>("RotatedCamera.Position");
            this.Target = Data.GetData<Vector3>("RotatedCamera.Target");
            this.leftrightRot = Data.GetData<float>("RotatedCamera.LeftRightRot");
            this.updownRot = Data.GetData<float>("RotatedCamera.UpDownLot");

            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Engine.AspectRatio, 1.0f, farPlane);

            Box.DeserializeData(Data);

            if (Data.IsService)
            {
                Engine.Services.AddService(typeof(Camera3D), this);
            }
        }
    }
}